เรียนรู้วิธีใช้ React ErrorBoundaries เพื่อจัดการข้อผิดพลาดอย่างสวยงาม ป้องกันแอปพลิเคชันล่ม และมอบประสบการณ์ผู้ใช้ที่ดีขึ้นด้วยกลยุทธ์การกู้คืนที่แข็งแกร่ง
React ErrorBoundary: กลยุทธ์การแยกข้อผิดพลาดและการกู้คืน
ในโลกของการพัฒนา front-end ที่เปลี่ยนแปลงอยู่ตลอดเวลา โดยเฉพาะเมื่อทำงานกับเฟรมเวิร์กที่ซับซ้อนและเป็นแบบคอมโพเนนต์อย่าง React ข้อผิดพลาดที่ไม่คาดคิดเป็นสิ่งที่หลีกเลี่ยงไม่ได้ หากไม่ได้รับการจัดการอย่างถูกต้อง ข้อผิดพลาดเหล่านี้อาจทำให้แอปพลิเคชันล่มและสร้างประสบการณ์ที่ไม่ดีให้กับผู้ใช้ คอมโพเนนต์ ErrorBoundary ของ React นำเสนอโซลูชันที่แข็งแกร่งสำหรับการจัดการข้อผิดพลาดเหล่านี้อย่างสวยงาม แยกข้อผิดพลาด และจัดเตรียมกลยุทธ์การกู้คืน คู่มือฉบับสมบูรณ์นี้จะสำรวจพลังของ ErrorBoundary และสาธิตวิธีการนำไปใช้อย่างมีประสิทธิภาพเพื่อสร้างแอปพลิเคชัน React ที่ยืดหยุ่นและเป็นมิตรกับผู้ใช้มากขึ้นสำหรับผู้ชมทั่วโลก
ทำความเข้าใจถึงความจำเป็นของ Error Boundaries
ก่อนที่จะลงลึกถึงการนำไปใช้ เรามาทำความเข้าใจกันก่อนว่าทำไม error boundaries ถึงจำเป็น ใน React ข้อผิดพลาดที่เกิดขึ้นระหว่างการ render ใน lifecycle methods หรือใน constructors ของคอมโพเนนต์ลูก อาจทำให้ทั้งแอปพลิเคชันล่มได้ นี่เป็นเพราะข้อผิดพลาดที่ไม่ถูกดักจับจะแพร่กระจายขึ้นไปตามลำดับชั้นของคอมโพเนนต์ (component tree) ซึ่งมักจะทำให้เกิดหน้าจอขาวหรือข้อความแสดงข้อผิดพลาดที่ไม่เป็นประโยชน์ ลองนึกภาพผู้ใช้ในญี่ปุ่นที่พยายามทำธุรกรรมทางการเงินที่สำคัญ แต่กลับต้องเจอกับหน้าจอขาวเนื่องจากข้อผิดพลาดเล็กน้อยในคอมโพเนนต์ที่ดูเหมือนจะไม่เกี่ยวข้อง สิ่งนี้แสดงให้เห็นถึงความจำเป็นอย่างยิ่งในการจัดการข้อผิดพลาดเชิงรุก
Error boundaries เป็นวิธีในการดักจับข้อผิดพลาด JavaScript ที่เกิดขึ้นที่ใดก็ได้ใน component tree ของคอมโพเนนต์ลูก บันทึกข้อผิดพลาดเหล่านั้น และแสดง UI สำรองแทนที่จะทำให้ component tree ล่ม ช่วยให้คุณสามารถแยกคอมโพเนนต์ที่ผิดพลาดและป้องกันไม่ให้ข้อผิดพลาดในส่วนหนึ่งของแอปพลิเคชันส่งผลกระทบต่อส่วนอื่นๆ ทำให้มั่นใจได้ว่าผู้ใช้จะได้รับประสบการณ์ที่เสถียรและเชื่อถือได้มากขึ้นทั่วโลก
React ErrorBoundary คืออะไร?
ErrorBoundary คือคอมโพเนนต์ React ที่ดักจับข้อผิดพลาด JavaScript ที่เกิดขึ้นที่ใดก็ได้ใน component tree ของคอมโพเนนต์ลูก บันทึกข้อผิดพลาดเหล่านั้น และแสดง UI สำรอง มันคือ class component ที่ต้อง implement lifecycle method อย่างน้อยหนึ่งในสองเมธอดต่อไปนี้:
static getDerivedStateFromError(error): lifecycle method นี้จะถูกเรียกใช้หลังจากที่คอมโพเนนต์ลูก (descendant component) เกิดข้อผิดพลาดขึ้น มันจะได้รับ error ที่เกิดขึ้นเป็นอาร์กิวเมนต์ และควรจะ return ค่าเพื่ออัปเดต state ของคอมโพเนนต์componentDidCatch(error, info): lifecycle method นี้จะถูกเรียกใช้หลังจากที่คอมโพเนนต์ลูกเกิดข้อผิดพลาดขึ้น มันจะได้รับสองอาร์กิวเมนต์คือ error ที่เกิดขึ้น และอ็อบเจกต์ info ที่มีข้อมูลเกี่ยวกับคอมโพเนนต์ที่ทำให้เกิดข้อผิดพลาด คุณสามารถใช้เมธอดนี้เพื่อบันทึกข้อมูลข้อผิดพลาดหรือดำเนินการ side effects อื่นๆ ได้
การสร้างคอมโพเนนต์ ErrorBoundary พื้นฐาน
เรามาสร้างคอมโพเนนต์ ErrorBoundary พื้นฐานเพื่อแสดงหลักการพื้นฐานกัน
ตัวอย่างโค้ด
นี่คือโค้ดสำหรับคอมโพเนนต์ ErrorBoundary แบบง่ายๆ:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null,
};
}
static getDerivedStateFromError(error) {
// อัปเดต state เพื่อให้การ render ครั้งถัดไปแสดง UI สำรอง
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// ตัวอย่าง "componentStack":
// in ComponentThatThrows (created by App)
// in App
console.error("Caught an error:", error);
console.error("Error info:", info.componentStack);
this.setState({ error: error, errorInfo: info });
// คุณยังสามารถบันทึกข้อผิดพลาดไปยังบริการรายงานข้อผิดพลาดได้
// logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// คุณสามารถ render UI สำรองที่กำหนดเองได้
return (
มีบางอย่างผิดปกติเกิดขึ้น
ข้อผิดพลาด: {this.state.error && this.state.error.toString()}
{this.state.errorInfo && this.state.errorInfo.componentStack}
);
}
return this.props.children;
}
}
export default ErrorBoundary;
คำอธิบาย
- Constructor: constructor จะเริ่มต้น state ของคอมโพเนนต์ด้วย
hasErrorที่ตั้งค่าเป็นfalseเรายังเก็บ error และ errorInfo ไว้เพื่อวัตถุประสงค์ในการดีบัก getDerivedStateFromError(error): static method นี้จะถูกเรียกใช้เมื่อมีข้อผิดพลาดเกิดขึ้นโดยคอมโพเนนต์ลูก มันจะอัปเดต state เพื่อระบุว่ามีข้อผิดพลาดเกิดขึ้นcomponentDidCatch(error, info): method นี้จะถูกเรียกใช้หลังจากเกิดข้อผิดพลาดขึ้น มันจะได้รับ error และอ็อบเจกต์infoที่มีข้อมูลเกี่ยวกับ component stack ที่นี่เราบันทึกข้อผิดพลาดไปยัง console (แทนที่ด้วยกลไกการบันทึกที่คุณต้องการ เช่น Sentry, Bugsnag หรือโซลูชันที่สร้างขึ้นเอง) เรายังตั้งค่า error และ errorInfo ใน state ด้วยrender(): render method จะตรวจสอบ statehasErrorหากเป็นtrueมันจะเรนเดอร์ UI สำรอง มิฉะนั้น มันจะเรนเดอร์ children ของคอมโพเนนต์ UI สำรองควรให้ข้อมูลและเป็นมิตรกับผู้ใช้ การรวมรายละเอียดข้อผิดพลาดและ component stack แม้จะเป็นประโยชน์สำหรับนักพัฒนา แต่ควรเรนเดอร์ตามเงื่อนไขหรือลบออกในสภาพแวดล้อม production เพื่อเหตุผลด้านความปลอดภัย
การใช้คอมโพเนนต์ ErrorBoundary
หากต้องการใช้คอมโพเนนต์ ErrorBoundary เพียงแค่ครอบคอมโพเนนต์ใดๆ ที่อาจทำให้เกิดข้อผิดพลาดด้วยคอมโพเนนต์นี้
ตัวอย่างโค้ด
import ErrorBoundary from './ErrorBoundary';
function MyComponent() {
return (
{/* คอมโพเนนต์ที่อาจเกิดข้อผิดพลาด */}
);
}
function App() {
return (
);
}
export default App;
คำอธิบาย
ในตัวอย่างนี้ MyComponent ถูกครอบด้วย ErrorBoundary หากมีข้อผิดพลาดใดๆ เกิดขึ้นภายใน MyComponent หรือคอมโพเนนต์ลูกของมัน ErrorBoundary จะดักจับข้อผิดพลาดนั้นและแสดง UI สำรอง
กลยุทธ์ ErrorBoundary ขั้นสูง
แม้ว่า ErrorBoundary พื้นฐานจะให้การจัดการข้อผิดพลาดในระดับพื้นฐาน แต่ก็มีกลยุทธ์ขั้นสูงหลายอย่างที่คุณสามารถนำไปใช้เพื่อปรับปรุงการจัดการข้อผิดพลาดของคุณได้
1. การใช้ Error Boundaries แบบเจาะจงส่วน (Granular)
แทนที่จะครอบทั้งแอปพลิเคชันด้วย ErrorBoundary เพียงตัวเดียว ลองพิจารณาใช้ error boundaries แบบเจาะจงส่วน ซึ่งเกี่ยวข้องกับการวางคอมโพเนนต์ ErrorBoundary รอบๆ ส่วนเฉพาะของแอปพลิเคชันของคุณที่มีแนวโน้มที่จะเกิดข้อผิดพลาดมากกว่า หรือส่วนที่ความล้มเหลวจะมีผลกระทบจำกัด ตัวอย่างเช่น คุณอาจครอบวิดเจ็ตแต่ละตัวหรือคอมโพเนนต์ที่ต้องพึ่งพาแหล่งข้อมูลภายนอก
ตัวอย่าง
function ProductList() {
return (
{/* รายการสินค้า */}
);
}
function RecommendationWidget() {
return (
{/* เอนจิ้นแนะนำสินค้า */}
);
}
function App() {
return (
);
}
ในตัวอย่างนี้ RecommendationWidget มี ErrorBoundary ของตัวเอง หากเอนจิ้นแนะนำสินค้าล้มเหลว มันจะไม่ส่งผลกระทบต่อ ProductList และผู้ใช้ยังสามารถเลือกดูสินค้าได้ต่อไป แนวทางแบบเจาะจงส่วนนี้ช่วยปรับปรุงประสบการณ์ผู้ใช้โดยรวมโดยการแยกข้อผิดพลาดและป้องกันไม่ให้มันลุกลามไปทั่วทั้งแอปพลิเคชัน
2. การบันทึกและรายงานข้อผิดพลาด
การบันทึกข้อผิดพลาดเป็นสิ่งสำคัญสำหรับการดีบักและระบุปัญหาที่เกิดขึ้นซ้ำๆ lifecycle method componentDidCatch เป็นที่ที่เหมาะสมที่สุดในการผสานรวมกับบริการบันทึกข้อผิดพลาด เช่น Sentry, Bugsnag หรือ Rollbar บริการเหล่านี้จะให้รายงานข้อผิดพลาดโดยละเอียด รวมถึง stack traces, context ของผู้ใช้ และข้อมูลสภาพแวดล้อม ทำให้คุณสามารถวินิจฉัยและแก้ไขปัญหาได้อย่างรวดเร็ว ควรพิจารณาการทำให้ข้อมูลผู้ใช้ที่ละเอียดอ่อนเป็นนิรนามหรือตัดออกก่อนที่จะส่งบันทึกข้อผิดพลาดเพื่อให้เป็นไปตามกฎระเบียบด้านความเป็นส่วนตัว เช่น GDPR
ตัวอย่าง
import * as Sentry from "@sentry/react";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
static getDerivedStateFromError(error) {
// อัปเดต state เพื่อให้การ render ครั้งถัดไปแสดง UI สำรอง
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// บันทึกข้อผิดพลาดไปยัง Sentry
Sentry.captureException(error, { extra: info });
// คุณยังสามารถบันทึกข้อผิดพลาดไปยังบริการรายงานข้อผิดพลาดได้
console.error("Caught an error:", error);
}
render() {
if (this.state.hasError) {
// คุณสามารถ render UI สำรองที่กำหนดเองได้
return (
มีบางอย่างผิดปกติเกิดขึ้น
);
}
return this.props.children;
}
}
export default ErrorBoundary;
ในตัวอย่างนี้ method componentDidCatch ใช้ Sentry.captureException เพื่อรายงานข้อผิดพลาดไปยัง Sentry คุณสามารถกำหนดค่า Sentry ให้ส่งการแจ้งเตือนไปยังทีมของคุณ ทำให้คุณสามารถตอบสนองต่อข้อผิดพลาดที่สำคัญได้อย่างรวดเร็ว
3. UI สำรองที่กำหนดเอง
UI สำรองที่แสดงโดย ErrorBoundary เป็นโอกาสในการมอบประสบการณ์ที่เป็นมิตรกับผู้ใช้แม้ในขณะที่เกิดข้อผิดพลาด แทนที่จะแสดงข้อความแสดงข้อผิดพลาดทั่วไป ลองพิจารณาแสดงข้อความที่ให้ข้อมูลมากขึ้นเพื่อแนะนำผู้ใช้ไปสู่แนวทางแก้ไข ซึ่งอาจรวมถึงคำแนะนำเกี่ยวกับวิธีการรีเฟรชหน้า ติดต่อฝ่ายสนับสนุน หรือลองอีกครั้งในภายหลัง คุณยังสามารถปรับแต่ง UI สำรองตามประเภทของข้อผิดพลาดที่เกิดขึ้นได้
ตัวอย่าง
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
};
}
static getDerivedStateFromError(error) {
// อัปเดต state เพื่อให้การ render ครั้งถัดไปแสดง UI สำรอง
return {
hasError: true,
error: error,
};
}
componentDidCatch(error, info) {
console.error("Caught an error:", error);
// คุณยังสามารถบันทึกข้อผิดพลาดไปยังบริการรายงานข้อผิดพลาดได้
// logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// คุณสามารถ render UI สำรองที่กำหนดเองได้
if (this.state.error instanceof NetworkError) {
return (
ข้อผิดพลาดเกี่ยวกับเครือข่าย
โปรดตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณแล้วลองอีกครั้ง
);
} else {
return (
มีบางอย่างผิดปกติเกิดขึ้น
โปรดลองรีเฟรชหน้าหรือติดต่อฝ่ายสนับสนุน
);
}
}
return this.props.children;
}
}
export default ErrorBoundary;
ในตัวอย่างนี้ UI สำรองจะตรวจสอบว่าข้อผิดพลาดนั้นเป็น NetworkError หรือไม่ ถ้าใช่ มันจะแสดงข้อความเฉพาะที่แนะนำให้ผู้ใช้ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของตน มิฉะนั้น มันจะแสดงข้อความแสดงข้อผิดพลาดทั่วไป การให้คำแนะนำที่เฉพาะเจาะจงและนำไปปฏิบัติได้สามารถปรับปรุงประสบการณ์ของผู้ใช้ได้อย่างมาก
4. กลไกการลองใหม่ (Retry Mechanisms)
ในบางกรณี ข้อผิดพลาดเป็นเพียงชั่วคราวและสามารถแก้ไขได้โดยการลองดำเนินการนั้นใหม่ คุณสามารถใช้กลไกการลองใหม่ภายใน ErrorBoundary เพื่อลองดำเนินการที่ล้มเหลวอีกครั้งโดยอัตโนมัติหลังจากเวลาที่กำหนด ซึ่งมีประโยชน์อย่างยิ่งสำหรับการจัดการกับข้อผิดพลาดของเครือข่ายหรือการหยุดทำงานของเซิร์ฟเวอร์ชั่วคราว ควรระมัดระวังในการใช้กลไกการลองใหม่กับการดำเนินการที่อาจมีผลข้างเคียง (side effects) เนื่องจากการลองใหม่อาจนำไปสู่ผลลัพธ์ที่ไม่พึงประสงค์ได้
ตัวอย่าง
import React, { useState, useEffect } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [retryCount, setRetryCount] = useState(0);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
setError(null);
} catch (e) {
setError(e);
setRetryCount(prevCount => prevCount + 1);
} finally {
setIsLoading(false);
}
};
if (error && retryCount < 3) {
const retryDelay = Math.pow(2, retryCount) * 1000; // การหน่วงเวลาแบบทวีคูณ
console.log(`กำลังลองใหม่ใน ${retryDelay / 1000} วินาที...`);
const timer = setTimeout(fetchData, retryDelay);
return () => clearTimeout(timer); // ล้าง timer เมื่อ unmount หรือ re-render
}
if (!data) {
fetchData();
}
}, [error, retryCount, data]);
if (isLoading) {
return กำลังโหลดข้อมูล...
;
}
if (error) {
return ข้อผิดพลาด: {error.message} - ลองใหม่แล้ว {retryCount} ครั้ง
;
}
return ข้อมูล: {JSON.stringify(data)}
;
}
function App() {
return (
);
}
export default App;
ในตัวอย่างนี้ DataFetchingComponent พยายามดึงข้อมูลจาก API หากเกิดข้อผิดพลาด มันจะเพิ่มค่า retryCount และลองดำเนินการอีกครั้งหลังจากหน่วงเวลาที่เพิ่มขึ้นแบบทวีคูณ ErrorBoundary จะดักจับ exception ที่ไม่ถูกจัดการและแสดงข้อความแสดงข้อผิดพลาด ซึ่งรวมถึงจำนวนครั้งที่พยายามลองใหม่ด้วย
5. Error Boundaries และ Server-Side Rendering (SSR)
เมื่อใช้ Server-Side Rendering (SSR) การจัดการข้อผิดพลาดจะยิ่งมีความสำคัญมากขึ้น ข้อผิดพลาดที่เกิดขึ้นระหว่างกระบวนการเรนเดอร์ฝั่งเซิร์ฟเวอร์อาจทำให้เซิร์ฟเวอร์ทั้งระบบล่มได้ นำไปสู่การหยุดทำงานและประสบการณ์ผู้ใช้ที่ไม่ดี คุณต้องแน่ใจว่า error boundaries ของคุณได้รับการกำหนดค่าอย่างถูกต้องเพื่อดักจับข้อผิดพลาดทั้งบนเซิร์ฟเวอร์และไคลเอ็นต์ บ่อยครั้งที่เฟรมเวิร์ก SSR อย่าง Next.js และ Remix มีกลไกการจัดการข้อผิดพลาดในตัวซึ่งทำงานเสริมกับ React Error Boundaries
6. การทดสอบ Error Boundaries
การทดสอบ error boundaries เป็นสิ่งจำเป็นเพื่อให้แน่ใจว่าพวกมันทำงานได้อย่างถูกต้องและแสดง UI สำรองตามที่คาดไว้ ใช้ไลบรารีการทดสอบเช่น Jest และ React Testing Library เพื่อจำลองสถานการณ์ข้อผิดพลาดและตรวจสอบว่า error boundaries ของคุณดักจับข้อผิดพลาดและแสดง UI สำรองที่เหมาะสมหรือไม่ พิจารณาทดสอบข้อผิดพลาดประเภทต่างๆ และกรณีพิเศษ (edge cases) เพื่อให้แน่ใจว่า error boundaries ของคุณมีความแข็งแกร่งและสามารถจัดการกับสถานการณ์ที่หลากหลายได้
ตัวอย่าง
import { render, screen } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';
function ComponentThatThrows() {
throw new Error('This component throws an error');
return This should not be rendered
;
}
test('renders fallback UI when an error is thrown', () => {
render(
);
const errorMessage = screen.getByText(/Something went wrong/i);
expect(errorMessage).toBeInTheDocument();
});
การทดสอบนี้จะเรนเดอร์คอมโพเนนต์ที่ทำให้เกิดข้อผิดพลาดภายใน ErrorBoundary จากนั้นจะตรวจสอบว่า UI สำรองถูกเรนเดอร์อย่างถูกต้องหรือไม่ โดยการตรวจสอบว่ามีข้อความแสดงข้อผิดพลาดปรากฏในเอกสารหรือไม่
7. การลดระดับการทำงานอย่างสวยงาม (Graceful Degradation)
Error boundaries เป็นองค์ประกอบสำคัญในการนำ Graceful Degradation มาใช้ในแอปพลิเคชัน React ของคุณ Graceful Degradation คือแนวปฏิบัติในการออกแบบแอปพลิเคชันให้ยังคงทำงานต่อไปได้ แม้ว่าฟังก์ชันการทำงานจะลดลงเมื่อบางส่วนของแอปพลิเคชันล้มเหลว Error boundaries ช่วยให้คุณสามารถแยกคอมโพเนนต์ที่ล้มเหลวและป้องกันไม่ให้ส่งผลกระทบต่อส่วนที่เหลือของแอปพลิเคชัน ด้วยการจัดเตรียม UI สำรองและฟังก์ชันการทำงานทางเลือก คุณสามารถมั่นใจได้ว่าผู้ใช้ยังคงสามารถเข้าถึงคุณสมบัติที่จำเป็นได้แม้ในขณะที่เกิดข้อผิดพลาด
ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง
แม้ว่า ErrorBoundary จะเป็นเครื่องมือที่ทรงพลัง แต่ก็มีข้อผิดพลาดทั่วไปบางประการที่ควรหลีกเลี่ยง:
- ไม่ครอบโค้ด asynchronous:
ErrorBoundaryจะดักจับข้อผิดพลาดเฉพาะระหว่างการเรนเดอร์ ใน lifecycle methods และใน constructors เท่านั้น ข้อผิดพลาดในโค้ด asynchronous (เช่นsetTimeout,Promises) จำเป็นต้องดักจับโดยใช้try...catchblocks และจัดการอย่างเหมาะสมภายในฟังก์ชัน asynchronous นั้นๆ - ใช้ Error Boundaries มากเกินไป: หลีกเลี่ยงการครอบส่วนใหญ่ของแอปพลิเคชันของคุณด้วย
ErrorBoundaryเพียงตัวเดียว ซึ่งอาจทำให้การระบุแหล่งที่มาของข้อผิดพลาดทำได้ยาก และอาจทำให้ UI สำรองทั่วไปแสดงบ่อยเกินไป ควรใช้ error boundaries แบบเจาะจงส่วนเพื่อแยกคอมโพเนนต์หรือคุณสมบัติเฉพาะ - ละเลยข้อมูลข้อผิดพลาด: อย่าเพียงแค่ดักจับข้อผิดพลาดและแสดง UI สำรอง ตรวจสอบให้แน่ใจว่าได้บันทึกข้อมูลข้อผิดพลาด (รวมถึง component stack) ไปยังบริการรายงานข้อผิดพลาดหรือ console ของคุณ ซึ่งจะช่วยให้คุณวินิจฉัยและแก้ไขปัญหาพื้นฐานได้
- แสดงข้อมูลที่ละเอียดอ่อนใน Production: หลีกเลี่ยงการแสดงข้อมูลข้อผิดพลาดโดยละเอียด (เช่น stack traces) ในสภาพแวดล้อม production ซึ่งอาจเปิดเผยข้อมูลที่ละเอียดอ่อนต่อผู้ใช้และอาจเป็นความเสี่ยงด้านความปลอดภัย ควรแสดงข้อความแสดงข้อผิดพลาดที่เป็นมิตรต่อผู้ใช้และบันทึกข้อมูลโดยละเอียดไปยังบริการรายงานข้อผิดพลาดแทน
Error Boundaries กับ Functional Components และ Hooks
แม้ว่า Error Boundaries จะถูก implement เป็น class component แต่คุณยังสามารถใช้มันเพื่อจัดการข้อผิดพลาดภายใน functional component ที่ใช้ hooks ได้อย่างมีประสิทธิภาพ วิธีการทั่วไปคือการครอบ functional component ด้วย ErrorBoundary component ดังที่ได้สาธิตไปก่อนหน้านี้ ตรรกะการจัดการข้อผิดพลาดจะอยู่ใน ErrorBoundary ซึ่งช่วยแยกข้อผิดพลาดที่อาจเกิดขึ้นระหว่างการเรนเดอร์ของ functional component หรือการทำงานของ hooks ได้อย่างมีประสิทธิภาพ
โดยเฉพาะอย่างยิ่ง ข้อผิดพลาดใดๆ ที่เกิดขึ้นระหว่างการเรนเดอร์ของ functional component หรือภายใน body ของ useEffect hook จะถูกดักจับโดย ErrorBoundary อย่างไรก็ตาม สิ่งสำคัญที่ควรทราบคือ ErrorBoundaries ไม่ได้ดักจับข้อผิดพลาดที่เกิดขึ้นภายใน event handlers (เช่น onClick, onChange) ที่ผูกอยู่กับองค์ประกอบ DOM ภายใน functional component สำหรับ event handlers คุณควรใช้ try...catch block แบบดั้งเดิมต่อไปสำหรับการจัดการข้อผิดพลาด
การทำให้ข้อความแสดงข้อผิดพลาดเป็นสากลและรองรับท้องถิ่น (Internationalization and Localization)
เมื่อพัฒนาแอปพลิเคชันสำหรับผู้ชมทั่วโลก การทำให้ข้อความแสดงข้อผิดพลาดของคุณเป็นสากลและรองรับท้องถิ่นเป็นสิ่งสำคัญอย่างยิ่ง ข้อความแสดงข้อผิดพลาดที่แสดงใน UI สำรองของ ErrorBoundary ควรได้รับการแปลเป็นภาษาที่ผู้ใช้ต้องการเพื่อมอบประสบการณ์ผู้ใช้ที่ดีขึ้น คุณสามารถใช้ไลบรารีอย่าง i18next หรือ React Intl เพื่อจัดการคำแปลของคุณและแสดงข้อความแสดงข้อผิดพลาดที่เหมาะสมแบบไดนามิกตามภาษาท้องถิ่น (locale) ของผู้ใช้
ตัวอย่างการใช้ i18next
import i18next from 'i18next';
import { useTranslation } from 'react-i18next';
i18next.init({
resources: {
en: {
translation: {
'error.generic': 'Something went wrong. Please try again later.',
'error.network': 'Network error. Please check your internet connection.',
},
},
fr: {
translation: {
'error.generic': 'Une erreur est survenue. Veuillez réessayer plus tard.',
'error.network': 'Erreur réseau. Veuillez vérifier votre connexion Internet.',
},
},
},
lng: 'en',
fallbackLng: 'en',
interpolation: {
escapeValue: false, // ไม่จำเป็นสำหรับ react เนื่องจากมีการ escape ตามค่าเริ่มต้น
},
});
function ErrorFallback({ error }) {
const { t } = useTranslation();
let errorMessageKey = 'error.generic';
if (error instanceof NetworkError) {
errorMessageKey = 'error.network';
}
return (
{t('error.generic')}
{t(errorMessageKey)}
);
}
function ErrorBoundary({ children }) {
const [hasError, setHasError] = useState(false);
const [error, setError] = useState(null);
static getDerivedStateFromError = (error) => {
// อัปเดต state เพื่อให้การ render ครั้งถัดไปแสดง UI สำรอง
// return { hasError: true }; // แบบนี้จะใช้กับ hooks ไม่ได้
setHasError(true);
setError(error);
}
if (hasError) {
// คุณสามารถ render UI สำรองที่กำหนดเองได้
return ;
}
return children;
}
export default ErrorBoundary;
ในตัวอย่างนี้ เราใช้ i18next เพื่อจัดการคำแปลสำหรับภาษาอังกฤษและฝรั่งเศส คอมโพเนนต์ ErrorFallback ใช้ hook useTranslation เพื่อดึงข้อความแสดงข้อผิดพลาดที่เหมาะสมตามภาษาปัจจุบัน สิ่งนี้ทำให้มั่นใจได้ว่าผู้ใช้จะเห็นข้อความแสดงข้อผิดพลาดในภาษาที่ต้องการ ซึ่งช่วยยกระดับประสบการณ์ผู้ใช้โดยรวม
สรุป
คอมโพเนนต์ ErrorBoundary ของ React เป็นเครื่องมือสำคัญสำหรับการสร้างแอปพลิเคชัน React ที่แข็งแกร่งและเป็นมิตรกับผู้ใช้ ด้วยการนำ error boundaries มาใช้ คุณสามารถจัดการข้อผิดพลาดอย่างสวยงาม ป้องกันแอปพลิเคชันล่ม และมอบประสบการณ์ที่ดีขึ้นสำหรับผู้ใช้ทั่วโลก ด้วยการทำความเข้าใจหลักการของ error boundaries การนำกลยุทธ์ขั้นสูงมาใช้ เช่น การใช้ error boundaries แบบเจาะจงส่วน การบันทึกข้อผิดพลาด และ UI สำรองที่กำหนดเอง และการหลีกเลี่ยงข้อผิดพลาดทั่วไป คุณจะสามารถสร้างแอปพลิเคชัน React ที่ยืดหยุ่นและเชื่อถือได้มากขึ้น ซึ่งตอบสนองความต้องการของผู้ชมทั่วโลก อย่าลืมพิจารณาการทำให้เป็นสากลและรองรับท้องถิ่นเมื่อแสดงข้อความแสดงข้อผิดพลาดเพื่อมอบประสบการณ์ผู้ใช้ที่ครอบคลุมอย่างแท้จริง ในขณะที่ความซับซ้อนของเว็บแอปพลิเคชันยังคงเพิ่มขึ้นอย่างต่อเนื่อง การเรียนรู้เทคนิคการจัดการข้อผิดพลาดให้เชี่ยวชาญจะมีความสำคัญมากขึ้นสำหรับนักพัฒนาที่สร้างซอฟต์แวร์คุณภาพสูง